﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml.Serialization;

namespace Ogdi.Data.DataLoader
{
    public enum SourceDataType 
    { 
        DbfAndKml, 
        Csv 
    }
    
    public enum DataLoadingTarget 
    { 
        Console, 
        Tables 
    }

    //What to do if data set is already in storage (only for TableDataLoader and TableEntityProcessor)
    public enum TableOverwriteMode
    {
        Create, //wipe and reload
        Add, //add to existing data set (must exist, must have identical metadata except for last updated date which should be newer and be updated), raise exception when inserting entity with the same rawID
        Update //update or add data to existing data set (conditions  should be the same as in add, update entity if exist, add if not)
    }

    public static class DataLoaderFactory
    {
        public static IDataLoader CreateDataLoader(SourceDataType type, DataLoadingTarget target, string fileSetName, TableOverwriteMode overwriteMode,bool sourceOrder)
        {
            EntityProducer producer = null;
            EntityProcessor processor = null;
            XmlSerializer serializer = null;
            DataLoader loader = null;

            string dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            string file = fileSetName + DataLoaderConstants.FileExtConfig;

            using (var stream = File.Open(Path.Combine(dir, file), FileMode.Open, FileAccess.Read))
            {
                switch (type)
                {
                    case SourceDataType.DbfAndKml:
                        {
                            switch (target)
                            {
                                case DataLoadingTarget.Console:
                                    {
                                        serializer = new XmlSerializer(typeof(DbaseKmlDataLoaderParams));
                                        var p = (DbaseKmlDataLoaderParams)serializer.Deserialize(stream);
                                        producer = new DbaseKmlEntityProducer(
                                                            fileSetName, 
                                                            p.TableMetadataEntity.EntitySet, 
                                                            p.TableMetadataEntity.EntityKind, 
                                                            p.ProducerParams,
                                                            sourceOrder);
                                        processor = new CoutEntityProcessor();
                                        loader = new DataLoader(p, producer, processor);
                                        break;
                                    }

                                case DataLoadingTarget.Tables:
                                    {
                                        serializer = new XmlSerializer(typeof(DbaseKmlToTablesDataLoaderParams));
                                        var p = (DbaseKmlToTablesDataLoaderParams)serializer.Deserialize(stream);
                                        if (p.ProcessorParams != null)
                                        {
                                            p.ProcessorParams.EntityKind = p.TableMetadataEntity.EntityKind;
                                            p.ProcessorParams.EntitySet = p.TableMetadataEntity.EntitySet;
                                        }
                                        producer = new DbaseKmlEntityProducer(
                                                            fileSetName, 
                                                            p.TableMetadataEntity.EntitySet,
                                                            p.TableMetadataEntity.EntityKind, 
                                                            p.ProducerParams,
                                                            sourceOrder);
                                        processor = new TableEntityProcessor(p.ProcessorParams, overwriteMode);
                                        loader = new TableDataLoader(p, producer, processor, overwriteMode);
                                        break;
                                    }
                            }

                            break;
                        }

                    case SourceDataType.Csv:
                        {
                            switch (target)
                            {
                                case DataLoadingTarget.Console:
                                    {
                                        serializer = new XmlSerializer(typeof(CsvDataLoaderParams));
                                        var p = (CsvDataLoaderParams)serializer.Deserialize(stream);
                                        producer = new CsvEntityProducer(
                                                            fileSetName, 
                                                            p.TableMetadataEntity.EntitySet,
                                                            p.TableMetadataEntity.EntityKind, 
                                                            p.ProducerParams,
                                                            sourceOrder);
                                        processor = new CoutEntityProcessor();
                                        loader = new DataLoader(p, producer, processor);
                                        break;
                                    }

                                case DataLoadingTarget.Tables:
                                    {
                                        serializer = new XmlSerializer(typeof(CsvToTablesDataLoaderParams));
                                        var p = (CsvToTablesDataLoaderParams)serializer.Deserialize(stream);
                                        if (p.ProcessorParams != null)
                                        {
                                            p.ProcessorParams.EntityKind = p.TableMetadataEntity.EntityKind;
                                            p.ProcessorParams.EntitySet = p.TableMetadataEntity.EntitySet;
                                        }

                                        if (sourceOrder)
                                            if (!String.IsNullOrEmpty(p.ProcessorParams.PartitionKeyPropertyName)
                                                && p.ProcessorParams.PartitionKeyPropertyName.ToLower() != DataLoaderConstants.ValueUniqueAutoGen.ToLower())
                                                throw new ApplicationException("PartitionKey must be empty or equal to 'New.Guid' in .cfg when use sourceOrder");

                                        if (overwriteMode == TableOverwriteMode.Update)
                                            if (String.IsNullOrEmpty(p.ProcessorParams.PartitionKeyPropertyName)
                                                || p.ProcessorParams.PartitionKeyPropertyName.ToLower() == DataLoaderConstants.ValueUniqueAutoGen.ToLower()
                                                || String.IsNullOrEmpty(p.ProcessorParams.PartitionKeyPropertyName)
                                                || p.ProcessorParams.PartitionKeyPropertyName.ToLower() == DataLoaderConstants.ValueUniqueAutoGen.ToLower())
                                                throw new ApplicationException("PartitionKey and RowKey must be provided in .cfg for /mode=update");
                                        
                                        producer = new CsvEntityProducer(
                                                            fileSetName, 
                                                            p.TableMetadataEntity.EntitySet,
                                                            p.TableMetadataEntity.EntityKind, 
                                                            p.ProducerParams,
                                                            sourceOrder);
                                        processor = new TableEntityProcessor(p.ProcessorParams, overwriteMode);
                                        loader = new TableDataLoader(p, producer, processor, overwriteMode);
                                        break;
                                    }
                            }

                            break;
                        }
                }
            }

            return loader;
        }
    }
}
